# Copyright (C) 2007 Samuel Abels, http://debain.org
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
import os.path, re
import xml.dom
from xml.dom.minidom import Document, Text
from Text2Xml.FooLib import UrlParser

attrib  = r'(?:[\$\w\-]+(?:=\"[^"]*\"|=[\$\w\-]*)?)'
path_re = re.compile(r'^[^/"\?]+(?:\?%s?(?:&%s?)*)?' % (attrib, attrib))

class XmlGenerator(object):
    def __init__(self, module, **kwargs):
        if kwargs.get('debug', 0) > 0:
            print "Loading parser...",
        from pytextparser import TextParser
        if kwargs.get('debug', 0) > 0:
            print "done."

        if kwargs.get('debug', 0) > 0:
            print "Initializing parser with %s..." % module,
        self.parser = TextParser(os.path.abspath(module))
        self.output = None
        self.node   = None
        self.nodes  = {}
        assert self.parser is not None
        if kwargs.get('debug', 0) > 0:
            print "done."


    def _splitpath(self, path):
        match  = path_re.match(path)
        result = []
        while match is not None:
            result.append(match.group(0))
            path = path[len(match.group(0)) + 1:]
            match  = path_re.match(path)
        return result


    def _nodehash(self, node):
        attrs = []
        if node.attributes is not None:
            for i in range(node.attributes.length):
                attr = node.attributes.item(i)
                attrs.append(attr.nodeName + '="%s"' % attr.nodeValue)
        return node.tagName + '?' + '&'.join(attrs)


    def _get_node_path(self, node):
        path = []
        while node.nodeType != xml.dom.Node.DOCUMENT_NODE:
            path.append(self._nodehash(node))
            node = node.parentNode
        path.reverse()
        return '/' + '/'.join(path)


    def _current_depth(self):
        i = 0
        node = self.node
        while node:
            node = node.parentNode
            i += 1
        return i


    def _create_child(self, node, name):
        #print "NAME:", name
        (protocol,
         user,
         password,
         tag_name,
         attrs) = UrlParser.parse_url(name)
        parent_path = self._get_node_path(node)
        tag_path    = '%s/%s' % (parent_path, name)
        tag_name    = tag_name.replace(' ', '-')
        child       = self.nodes.get(tag_path, None)
        #print "CREATE:", tag_path, tag_name, attrs
        if child is None:
            child = self.output.createElement(tag_name)
            self.node.appendChild(child)
            self.nodes[tag_path] = child
        for key, value in attrs.iteritems():
            value = value[0]
            value = value.strip('"')
            child.setAttribute(key, value)
        return child


    def _callback(self, toktype, name):
        #print "  " * self._current_depth() + "T:", toktype, name
        if toktype == 0:  # Open.
            path = self._splitpath(name)
            for name in path:
                self.node = self._create_child(self.node, name)
        elif toktype == 1: # Close.
            path = self._splitpath(name)
            for name in path:
                self.node = self.node.parentNode
        elif toktype == 2:  # Single.
            path = self._splitpath(name)
            for name in path:
                self.node = self._create_child(self.node, name)
            for name in path:
                self.node = self.node.parentNode
        elif toktype == 3:  # Data.
            if name == '':
                return
            new_node = self.node.firstChild
            if new_node is None:
                new_node = self.output.createTextNode(name)
                self.node.appendChild(new_node)
            elif not isinstance(new_node, Text):
                print "Unexpected child at %s" % name
                print self.node.toprettyxml()
                assert False
            else:
                new_node.replaceWholeText(name)
        else:  # Data (appending).
            if name == '':
                return
            new_node = self.output.createTextNode(name)
            self.node.appendChild(new_node)


    def convert(self, file):
        self.output = Document()
        self.node   = self.output.createElement('xml')
        self.nodes  = {}
        self.output.appendChild(self.node)
        fp = open(file, 'r')
        self.parser.parse(fp.read(), self._callback)
        fp.close()
        return self.output.toprettyxml(indent="  ")
